﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Windows.Forms;
using PostProcDirectX;
using System.IO;
using PostProcCalc;

namespace Postproc_directX
{
    class DirectX : IGraphic
    {
        //данные окна
        public float width;//ширина
        public float height;//высота
        public float dx = 0;//смещение по оси ox
        public float dy = 0;//смещение по оси oy
        public float zoom = 1;//масштаб

        //Свойства
        public float Width
        {
            set { width = value; }
            get { return width; }
        }
        public float Height
        {
            set { height = value; }
            get { return height; }
        }
        public float Dx
        {
            set { dx = value; }
            get { return dx; }
        }
        public float Dy
        {
            set { dy = value; }
            get { return dy; }
        }
        public float Zoom
        {
            set { zoom = value; }
            get { return zoom; }
        }

        public IMeshFEM MeshFem
        {
            set { throw new NotImplementedException(); }
        }

        public List<Tuple<Point, double>> UserPoints
        {
            set { throw new NotImplementedException(); }
        }

        public void SaveFrame(Stream stream)
        {
            throw new NotImplementedException();
        }

        //начальная инициализация графики
        public void InitializeGraphics(Panel DrawArea)
        {
            try
            {
                // Устанавливаем параметры презентации
                PresentParameters presentParams = new PresentParameters();
                presentParams.Windowed = true;
                presentParams.SwapEffect = SwapEffect.Discard;
                // Создаем устройство
                device = new Device(0, DeviceType.Hardware, DrawArea, CreateFlags.SoftwareVertexProcessing, presentParams);
                this.width = DrawArea.Width;
                this.height = DrawArea.Height;
           
                // Создать вершинный буфер
                vb = new VertexBuffer(typeof(CustomVertex.PositionColored),
                                      3,
                                      device,
                                      Usage.Dynamic | Usage.WriteOnly,
                                      CustomVertex.PositionColored.Format,
                                      Pool.Default);
                vb.Created += new EventHandler(vb_Created);

                // Принудительный вызов создания треугольника 
                // и заполнения вершинного буфера при первом 
                // создании формы
                vb_Created(vb, null);
            }
            catch (Exception ex)
            {
                
            }
        }

        //задание данных
        public void SetData(List<List<int>> nvtr, List<float> X,List<float> Y, List<float> u)
        {
            
            this.nvtr = nvtr;
            this.XY = new List<PointF>();
            
            int i;
            for (i = 0; i < X.Count; i++)
                XY.Add(new PointF(X[i], Y[i]));

            this.u = u;
            Colors = new List<int>();
            uMin = u.Min();
            uMax = u.Max();
            for (i = 0; i < u.Count; i++)
            {
                Colors.Add(getColor(u[i], uMin, uMax));
            }

            UpdateVertexBuffer();
        }

        //отрисовка всей сцены
        public void OnPaint()
        {
            try
            {
                device.Clear(ClearFlags.Target, Color.White, 1.0f, 0);
                SetupCamera();

                // Сформировать сцену с учетом параметров 
                // "положение-цвет"
                device.BeginScene();
                // Установить формат обработки вершин при отображении
                device.VertexFormat = CustomVertex.PositionColored.Format;
                // Нарисовать треугольник по данным из буфера
                device.SetStreamSource(0, vb, 0);
                device.DrawPrimitives(PrimitiveType.TriangleList, 0, nvtr.Count);
                device.EndScene();
                device.Present();

            }
            catch (Exception ex)
            {
                //
            }
        }

        public DirectX()
        { }
       
        private void vb_Created(object sender, EventArgs e)
        {
            UpdateVertexBuffer();
        }
        private void HSV2RGB(ref int r, ref int g, ref int b, int i_hue, int sat, int val)
        /* Вход:
        * 0 <= hue < 360 градусов - оттенок. Основные цвета:
        * 0 - красный, 60 - желтый,
        * 120 - зеленый, 180 - голубой
        * 240 - синий, 300 - пурпурный
        * hue == 360 - неопределён !!!
        * Остальные цвета между ними
        * 0 <= sat <= 255 - Saturation - насыщенность
        * 0 <= val <= 255 - Value - светлота
        *
        * Выход:
        * 0 <= r,g,b <= 255 - значения красного, зеленого, синего
        */
        {
            int ii;
            double fr, hue;
            int c1, c2, c3;
            if (sat == 0)
                r = g = b = val;
            else
            { /* Хроматический цвет */
                while (i_hue >= 360)
                    i_hue -= 360;
                hue = i_hue;
                ii = (int)((hue) /= 60.0);
                fr = hue - ii;
                c1 = (val * (255 - sat)) / 255;
                c2 = (val * (int)(255.0 - (double)sat * (fr))) / 255;
                c3 = (val * (int)(255.0 - (double)sat * (1.0 - fr))) / 255;
                switch (ii)
                {
                    case 0: r = val; g = c3; b = c1; break;
                    case 1: r = c2; g = val; b = c1; break;
                    case 2: r = c1; g = val; b = c3; break;
                    case 3: r = c1; g = c2; b = val; break;
                    case 4: r = c3; g = c1; b = val; break;
                    case 5: r = val; g = c1; b = c2; break;
                } // switch
            } // if (!sat)
        } // HSV 2 RGB
        private int getColor(double x, double x_min, double x_max)
        {
            int r = 0, g = 0, b = 0, hue;
            if (x > x_max) x = x_max;
            if (x < x_min) x = x_min;
            if ((x_max - x_min) > 1e-13)
                hue = 270 - (int)((x - x_min) / (x_max - x_min) * 270);
            else
                hue = 0;
            HSV2RGB(ref r, ref g, ref b, hue, 255, 255);
            return System.Drawing.Color.FromArgb(r, g, b).ToArgb(); 
        }
        List<int> Colors;
        float uMin, uMax;
        
        //Перевод из мир. в экр. координаты
        public  Vector2 WorldSpaceToScreenSpace(Vector3 worldSpacePos)
        {
            Vector2 res;
            //Vector3 v1, v2, v3;
            //Matrix m1, m2, m3;
            Vector3 vtmp = new Vector3();

            vtmp = Vector3.TransformCoordinate(worldSpacePos, device.Transform.View);
            //m1 = device.Transform.View;
            //m1.Invert();
            //v1 = Vector3.TransformCoordinate(vtmp, m1);


            vtmp = Vector3.TransformCoordinate(vtmp, device.Transform.Projection);
            //m2 = device.Transform.Projection;
            //m2.Invert();
            //v2 = Vector3.TransformCoordinate(vtmp, m2);


            Matrix move = Matrix.Translation(0, 0, 0) * Matrix.Scaling(zoom, zoom, 0) * Matrix.Translation(dx, dy, 0);
            vtmp = Vector3.TransformCoordinate(vtmp, move);

            //m3 = move;
            //m3.Invert();
            //v3 = Vector3.TransformCoordinate(vtmp, m3);



            res.X = (float)((vtmp.X + 1) * width / 2);
            res.Y = (float)((1 - vtmp.Y) * height / 2);

            

            return res;
        }
        //Перевод из экр. в мир. координаты
        public Vector3 ScreenSpaceToWorldSpace(Vector2 ScreenSpacePos)
        {
            //Нужно сделать, необходимо для позиционирования мыши м мировых координатах.

            Vector3 res = new Vector3();
            Matrix m1, m2, m3,move;
            Vector3 vtmp = new Vector3();
            //первое обратное преобразование
            vtmp.Z = 0;
            vtmp.Y = 1.0f - (2.0f * ScreenSpacePos.Y) / height;
            vtmp.X = (2.0f * ScreenSpacePos.X) / width - 1.0f;

            //второе
            move = Matrix.Translation(0, 0, 0) * Matrix.Scaling(1.0f/ zoom, 1.0f/ zoom, 0) * Matrix.Translation(-dx, -dy, 0);


            vtmp = Vector3.TransformCoordinate(vtmp, move);
            vtmp.Z = 0.9998979f;

            //третье
            m2 = device.Transform.Projection;
            m2.Invert();

            vtmp = Vector3.TransformCoordinate(vtmp, m2);
            vtmp.Z = 98.99999f;
            //четвёртое
            m1 = device.Transform.View;
            m1.Invert();
            vtmp = Vector3.TransformCoordinate(vtmp, m1);

            vtmp.Z = 1;

            res = vtmp;
            return res;
        }


        //************************Объявление переменных*****************
        //********Отображение
        private Device device = null;   //Устройство
        private VertexBuffer vb = null;
        PresentParameters presentParams = new PresentParameters();// параметры представления
        
        //*******Модель
        CustomVertex.PositionColored[] verts;
        List<PointF> XY; //Список координат узлов
        List<float> u; // Список значений в узлах
        List<List<int>> nvtr; //Список глобальных номеров узлов конечных элементов
        //**************************************************************

        // Установка камеры в сцену
        private void SetupCamera()
        {
            //Создание перспективы 
            device.Transform.Projection = Matrix.PerspectiveFovLH(
                (float)Math.PI / 4, // Угол зрения равен 45 градусов
                // Форматное соотношение сторон
                width / height,
                1.0F,               // Ближний планд
                100.0F);            // Дальний план

            //Добавление камеры 
            device.Transform.View = Matrix.LookAtLH(
                new Vector3(0, 0, 100.0F),    // Положение камеры
                new Vector3(0, 0, 0),              // Положение объекта текущее
                new Vector3(0, 1, 0));      // Направление камеры

            device.Transform.World = Matrix.Translation(0, 0, 0) * Matrix.Scaling(zoom, zoom, 0) * Matrix.Translation(dx, dy, 0);


            // Освещение не используем
            device.RenderState.Lighting = false;
            device.RenderState.CullMode = Cull.None;
        }

        private void UpdateVertexBuffer()
        {


            // Создать массив структур непреобразованных координат
            // треугольника с возможностью определения нормали к нему
            if (nvtr == null || XY == null || Colors == null)
                return;
            verts = new
                CustomVertex.PositionColored[nvtr.Count * 3];
            vb = new VertexBuffer(typeof(CustomVertex.PositionColored),
                                  3 * nvtr.Count,
                                  device,
                                  Usage.Dynamic | Usage.WriteOnly,
                                  CustomVertex.PositionColored.Format,
                                  Pool.Default);
            vb.Created += new EventHandler(vb_Created);
            for (int i = 0; i < nvtr.Count; i++)
            {
                verts[i * 3].Position = (new Vector3(XY[nvtr[i][0]].X, XY[nvtr[i][0]].Y, 1));
                verts[i * 3].Color = Colors[nvtr[i][0]];
                verts[i * 3 + 1].Position = (new Vector3(XY[nvtr[i][1]].X, XY[nvtr[i][1]].Y, 1));
                verts[i * 3 + 1].Color = Colors[nvtr[i][1]];
                verts[i * 3 + 2].Position = (new Vector3(XY[nvtr[i][2]].X, XY[nvtr[i][2]].Y, 1));
                verts[i * 3 + 2].Color = Colors[nvtr[i][2]];
            }



            // Заполнить вершинный буфер данными треугольника
            vb.SetData(verts, 0, LockFlags.None);
        }
    }


}
